package com.beiding.jsonanalyzer.analyze;

import org.apache.catalina.LifecycleState;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * 计时窗口
 */
public class TimeWindow implements Window {

    private ScheduledExecutorService service;

    /**
     * 初始化的时间批对象
     */
    private Long batch = -1L;

    private int timeMillis;

    public TimeWindow(int timeMillis) {
        if (timeMillis <= 0) {
            throw new IllegalArgumentException("计数窗口数量必须大于0");
        }
        this.timeMillis = timeMillis;
    }

    public static TimeWindow of(int timeMillis) {
        return new TimeWindow(timeMillis);
    }

    @Override
    public WindowInstance createInstance(Consumer<Object> open, Consumer<Object> close) {
        return new C(open, close);
    }

    synchronized
    private void addRunning(C c) {
        running.add(c);
        c.open.accept(batch);
        //检查是否执行任务
        if (service == null) {
            service = Executors.newScheduledThreadPool(1);
            //提交任务开始执行
            service.scheduleAtFixedRate(() -> {
                synchronized (TimeWindow.this) {
                    for (C c1 : running) {
                        c1.close.accept(batch);
                    }
                    batch++;
                    for (C c1 : running) {
                        c1.open.accept(batch);
                    }
                }
            }, timeMillis, timeMillis, TimeUnit.MILLISECONDS);

        }
    }

    synchronized
    private void removeRunning(C c) {
        running.remove(c);
        c.close.accept(batch);
        if (running.size() == 0) {
            service.shutdown();
            service = null;
        }
    }

    private List<C> running = new ArrayList<>();


    private class C implements WindowInstance {

        Consumer<Object> open;
        Consumer<Object> close;

        public C(Consumer<Object> open, Consumer<Object> close) {
            this.open = open;
            this.close = close;
        }

        @Override
        public void add(Runnable action) {
            action.run();
        }

        @Override
        public void start() {
            addRunning(this);
        }

        @Override
        public void end() {
            removeRunning(this);
        }
    }

}
